package com.gjy.mongo;

import com.mongodb.event.CommandFailedEvent;
import com.mongodb.event.CommandListener;
import com.mongodb.event.CommandStartedEvent;
import com.mongodb.event.CommandSucceededEvent;
import org.bson.BsonDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 慢日志监听器
 *
 * @author 宫静雨
 * @version 1.0
 * @since 2023-01-30 09:34:58
 */
public class SlowLogListener implements CommandListener {

    private static final Logger logger = LoggerFactory.getLogger(SlowLogListener.class);
    // 命令表最大大小
    private static final Long DEFAULT_LIMIT = 100000L;
    // 命令记录表
    private static final Map<Integer, BsonDocument> commands = new ConcurrentHashMap<>();
    // 命令时延阈值
    private final Long maxMs;
    // 记录表大小
    private final Long overLimit;

    public SlowLogListener(Long maxMs) {
        this(maxMs, DEFAULT_LIMIT);
    }

    public SlowLogListener(Long maxMs, Long overLimit) {
        this.maxMs = maxMs;
        this.overLimit = overLimit;
    }

    @Override
    public void commandStarted(CommandStartedEvent event) {
        if (commands.size() >= overLimit) {
            return;
        }
        commands.put(event.getRequestId(), event.getCommand());
    }

    @Override
    public void commandSucceeded(CommandSucceededEvent event) {
        // 检查命令时延
        long elapsedTime = event.getElapsedTime(TimeUnit.MILLISECONDS);
        if (elapsedTime < maxMs) {
            return;
        }
        BsonDocument command = commands.get(event.getRequestId());
        if (Objects.isNull(command)) {
            return;
        }
        logger.info("command succeeded - {}, spend {}ms, detail: {}",
                event.getCommandName(), elapsedTime, toJson(command));
        commands.remove(event.getRequestId());
    }

    @Override
    public void commandFailed(CommandFailedEvent event) {
        long elapsedTime = event.getElapsedTime(TimeUnit.MILLISECONDS);
        if (elapsedTime < maxMs) {
            return;
        }
        BsonDocument command = commands.get(event.getRequestId());
        if (Objects.isNull(command)) {
            return;
        }
        logger.error("command failed - {}, spend {}ms, detail: {}",
                event.getCommandName(), elapsedTime, toJson(command));
        commands.remove(event.getRequestId());
    }

    private String toJson(BsonDocument bson) {
        if (Objects.isNull(bson)) {
            return "EMPTY";
        }
        try {
            return bson.toJson();
        } catch (Exception e) {
            return "serialize failed by: " + e.getMessage();
        }
    }
}
